package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberResponseVo;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.enume.OrderStatusEnum;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WmsFeignService;
import com.atguigu.gulimall.order.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.to.SpuInfoVo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

	private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();

	@Autowired
	ProductFeignService productFeignService;

	@Autowired
	MemberFeignService memberFeignService;

	@Autowired
	CartFeignService cartFeignService;

	@Autowired
	ThreadPoolExecutor executor;

	@Autowired
	WmsFeignService wmsFeignService;


	@Autowired
	StringRedisTemplate redisTemplate;

	@Autowired
	OrderItemService orderItemService;

	@Override
	public PageUtils queryPage(Map<String, Object> params) {
		IPage<OrderEntity> page = this.page(
				new Query<OrderEntity>().getPage(params),
				new QueryWrapper<OrderEntity>()
		);

		return new PageUtils(page);
	}


	/**
	 * 订单确认页返回需要用的数据
	 *
	 * @return
	 */
	@Override
	public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
		//构建OrderConfirmVo
		OrderConfirmVo confirmVo = new OrderConfirmVo();


		//获取当前用户登录的信息
		MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();

		//TODO :获取当前线程请求头信息(解决Feign异步调用丢失请求头问题)
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

		CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {

			//每一个线程都来共享之前的请求数据
			RequestContextHolder.setRequestAttributes(requestAttributes);

			//1、远程查询所有的收获地址列表
			List<MemberAddressVo> address = memberFeignService.getAddress(memberResponseVo.getId());
			confirmVo.setMemberAddressVos(address);
		}, executor);


		CompletableFuture<Void> cartInfoFuture = CompletableFuture.runAsync(() -> {

			//每一个线程都来共享之前的请求数据
			RequestContextHolder.setRequestAttributes(requestAttributes);

			//2、远程查询购物车所有选中的购物项
			List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems();
			confirmVo.setItems(currentCartItems);
		}, executor).thenRunAsync(() -> {
			List<OrderItemVo> items = confirmVo.getItems();
			//获取全部商品的id
			List<Long> skuIds = items.stream()
					.map((itemVo -> itemVo.getSkuId()))
					.collect(Collectors.toList());
			//远程查询商品库存信息
			R skuHasStock = wmsFeignService.getSkuHasStock(skuIds);
			List<SkuStockVo> skuStockVos = skuHasStock.getData("data", new TypeReference<List<SkuStockVo>>() {
			});
			if (skuStockVos != null && skuStockVos.size() > 0) {
				//将skuStockVos集合转换为map
				Map<Long, Boolean> skuHasStockMap = skuStockVos.stream().collect(Collectors.toMap(SkuStockVo::getSkuId
						, SkuStockVo::getHasStock));
				confirmVo.setStocks(skuHasStockMap);
			}

		}, executor);


		//3、查询用户积分
		Integer integration = memberResponseVo.getIntegration();
		confirmVo.setIntegration(integration);

		//4.其他数据计算
		//TODO 5、防重令牌(防止表单重复提交)
		//为用户设置一个token，三十分钟过期时间（存在redis）
		String token = UUID.randomUUID().toString().replace("-", "");
		redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId(), token, 30,
				TimeUnit.MINUTES);
		confirmVo.setOrderToken(token);

		CompletableFuture.allOf(addressFuture, cartInfoFuture).get();

		return confirmVo;
	}

	/**
	 * 提交订单
	 * @param vo
	 * @return
	 */
	// @Transactional(isolation = Isolation.READ_COMMITTED) 设置事务的隔离级别
	// @Transactional(propagation = Propagation.REQUIRED)   设置事务的传播级别
	@GlobalTransactional(rollbackFor = Exception.class)
	@Transactional(rollbackFor = Exception.class)
	@Override
	public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {

		confirmVoThreadLocal.set(vo);

		SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
		//去创建、下订单、验令牌、验价格、锁定库存...

		//获取当前用户登录的信息
		MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
		responseVo.setCode(0);

		//1、验证令牌是否合法【令牌的对比和删除必须保证原子性】
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0" +
				" end";
		String orderToken = vo.getOrderToken();

		Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
				Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId()), orderToken);

		if (result == 0L) {
			//令牌验证失败
			responseVo.setCode(1);
			return responseVo;
		}else {
			//令牌验证成功
			//1、创建订单、订单项等信息
			OrderCreateTo order = createOrder();


			//2、验证价格
			BigDecimal payAmount = order.getOrder().getPayAmount();
			BigDecimal payPrice = vo.getPayPrice();

			if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {
				//金额对比
			//	...
				//TODO 3、保存订单
				saveOrder(order);

				//4、库存锁定,只要有异常，回滚订单数据
				//订单号、所有订单项信息(skuId,skuNum,skuName)
				WareSkuLockVo lockVo = new WareSkuLockVo();
				lockVo.setOrderSn(order.getOrder().getOrderSn());

				//获取出要锁定的商品数据信息
				List<OrderItemVo> orderItemVos = order.getOrderItems().stream().map((item) -> {
					OrderItemVo orderItemVo = new OrderItemVo();
					orderItemVo.setSkuId(item.getSkuId());
					orderItemVo.setCount(item.getSkuQuantity());
					orderItemVo.setTitle(item.getSkuName());
					return orderItemVo;
				}).collect(Collectors.toList());
				lockVo.setLocks(orderItemVos);

				//TODO 调用远程锁定库存的方法
				R r = wmsFeignService.orderLockStock(lockVo);
				if (r.getCode() == 0) {
					//锁定成功

					//锁定成功
					responseVo.setOrder(order.getOrder());
					// int i = 10/0;



				}else {
				//	锁定失败

					//锁定失败
					String msg = (String) r.get("msg");
					throw new NoStockException(msg);
					// responseVo.setCode(3);
					// return responseVo;

				}

			}else {
				responseVo.setCode(2);
				return responseVo;
			}

		}
		return responseVo;
	}

	@Override
	public OrderEntity getOrderByOrderSn(String orderSn) {

		OrderEntity orderEntity = this.baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));

		return orderEntity;

	}

	@Override
	public PayVo getOrderPay(String orderSn) {
		PayVo payVo = new PayVo();
		OrderEntity orderInfo = this.getOrderByOrderSn(orderSn);

		//保留两位小数点，向上取值
		BigDecimal payAmount = orderInfo.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
		payVo.setTotal_amount(payAmount.toString());
		payVo.setOut_trade_no(orderInfo.getOrderSn());

		//查询订单项的数据
		List<OrderItemEntity> orderItemInfo = orderItemService.list(
				new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
		OrderItemEntity orderItemEntity = orderItemInfo.get(0);
		payVo.setBody(orderItemEntity.getSkuAttrsVals());

		payVo.setSubject(orderItemEntity.getSkuName());

		return payVo;
	}

	private void saveOrder(OrderCreateTo orderCreateTo) {
		//获取订单信息
		OrderEntity order = orderCreateTo.getOrder();
		order.setModifyTime(new Date());
		order.setCreateTime(new Date());
		//保存订单
		this.baseMapper.insert(order);

		//获取订单项信息
		List<OrderItemEntity> orderItems = orderCreateTo.getOrderItems();
		//批量保存订单项数据
		orderItemService.saveBatch(orderItems);

	}

	private OrderCreateTo createOrder() {

		OrderCreateTo createTo = new OrderCreateTo();

		//1、生成订单号
		String orderSn = IdWorker.getTimeId();
		OrderEntity orderEntity = builderOrder(orderSn);

		//2、获取到所有的订单项
		List<OrderItemEntity> orderItemEntities = builderOrderItems(orderSn);

		//3、验价(计算价格、积分等信息)
		computePrice(orderEntity,orderItemEntities);

		createTo.setOrder(orderEntity);
		createTo.setOrderItems(orderItemEntities);

		return createTo;

	}

	/**
	 * 计算价格的方法
	 * @param orderEntity
	 * @param orderItemEntities
	 */
	private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
		//总价
		BigDecimal total = new BigDecimal("0.0");
		//优惠价
		BigDecimal coupon = new BigDecimal("0.0");
		BigDecimal intergration = new BigDecimal("0.0");
		BigDecimal promotion = new BigDecimal("0.0");

		//积分、成长值
		Integer integrationTotal = 0;
		Integer growthTotal = 0;

		//订单总额，叠加每一个订单项的总额信息
		for (OrderItemEntity orderItem : orderItemEntities) {
			//优惠价格信息
			coupon = coupon.add(orderItem.getCouponAmount());
			promotion = promotion.add(orderItem.getPromotionAmount());
			intergration = intergration.add(orderItem.getIntegrationAmount());

			//总价
			total = total.add(orderItem.getRealAmount());

			//积分信息和成长值信息
			integrationTotal += orderItem.getGiftIntegration();
			growthTotal += orderItem.getGiftGrowth();

		}

		//1、订单价格相关的
		orderEntity.setTotalAmount(total);
		//设置应付总额(总额+运费)
		orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
		orderEntity.setCouponAmount(coupon);
		orderEntity.setPromotionAmount(promotion);
		orderEntity.setIntegrationAmount(intergration);

		//设置积分成长值信息
		orderEntity.setIntegration(integrationTotal);
		orderEntity.setGrowth(growthTotal);

		//设置删除状态(0-未删除，1-已删除)
		orderEntity.setDeleteStatus(0);

	}

	/**
	 * 构建订单数据
	 * @param orderSn
	 * @return
	 */
	private OrderEntity builderOrder(String orderSn) {

		//获取当前用户登录信息
		MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();

		OrderEntity orderEntity = new OrderEntity();
		orderEntity.setMemberId(memberResponseVo.getId());
		orderEntity.setOrderSn(orderSn);
		orderEntity.setMemberUsername(memberResponseVo.getUsername());

		OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();

		//远程获取收货地址和运费信息
		R fareAddressVo = wmsFeignService.getFare(orderSubmitVo.getAddrId());
		FareVo fareResp = fareAddressVo.getData("data", new TypeReference<FareVo>() {});

		//获取到运费信息
		BigDecimal fare = fareResp.getFare();
		orderEntity.setFreightAmount(fare);

		//获取到收货地址信息
		MemberAddressVo address = fareResp.getAddress();


		//设置收货人信息
		orderEntity.setReceiverName(address.getName());
		orderEntity.setReceiverPhone(address.getPhone());
		orderEntity.setReceiverPostCode(address.getPostCode());
		orderEntity.setReceiverProvince(address.getProvince());
		orderEntity.setReceiverCity(address.getCity());
		orderEntity.setReceiverRegion(address.getRegion());
		orderEntity.setReceiverDetailAddress(address.getDetailAddress());

		//设置订单相关的状态信息
		orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
		orderEntity.setAutoConfirmDay(7);
		orderEntity.setConfirmStatus(0);
		return orderEntity;

	}

	/**
	 * 构建所有订单项数据
	 * @return
	 */
	public List<OrderItemEntity> builderOrderItems(String orderSn) {

		List<OrderItemEntity> orderItemEntityList = new ArrayList<>();

		//最后确定每个购物项的价格
		List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems();
		if (currentCartItems != null && currentCartItems.size() > 0) {
			orderItemEntityList = currentCartItems.stream().map((items) -> {
				//构建订单项数据
				OrderItemEntity orderItemEntity = builderOrderItem(items);
				orderItemEntity.setOrderSn(orderSn);

				return orderItemEntity;
			}).collect(Collectors.toList());
		}

		return orderItemEntityList;

	}

	/**
	 * 构建某一个订单项的数据
	 * @param items
	 * @return
	 */
	private OrderItemEntity builderOrderItem(OrderItemVo items) {
		OrderItemEntity orderItemEntity = new OrderItemEntity();

		//1、商品的spu信息
		Long skuId = items.getSkuId();
		//获取spu的信息
		R spuInfo = productFeignService.getSpuInfoBySkuId(skuId);
		SpuInfoVo spuInfoData = spuInfo.getData("data", new TypeReference<SpuInfoVo>() {
		});
		orderItemEntity.setSpuId(spuInfoData.getId());
		orderItemEntity.setSpuName(spuInfoData.getSpuName());
		orderItemEntity.setSpuBrand(spuInfoData.getBrandName());
		orderItemEntity.setCategoryId(spuInfoData.getCatalogId());

		//2、商品的sku信息
		orderItemEntity.setSkuId(skuId);
		orderItemEntity.setSkuName(items.getTitle());
		orderItemEntity.setSkuPic(items.getImage());
		orderItemEntity.setSkuPrice(items.getPrice());
		orderItemEntity.setSkuQuantity(items.getCount());

		//4、商品的积分信息
		orderItemEntity.setGiftGrowth(items.getPrice().multiply(new BigDecimal(items.getCount())).intValue());
		orderItemEntity.setGiftIntegration(items.getPrice().multiply(new BigDecimal(items.getCount())).intValue());

		//5、订单项的价格信息
		orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
		orderItemEntity.setCouponAmount(BigDecimal.ZERO);
		orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);

		//当前订单项的实际金额.总额 - 各种优惠价格
		//原来的价格
		BigDecimal origin = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
		//原价减去优惠价得到最终的价格
		BigDecimal subtract = origin.subtract(orderItemEntity.getCouponAmount())
				.subtract(orderItemEntity.getPromotionAmount())
				.subtract(orderItemEntity.getIntegrationAmount());
		orderItemEntity.setRealAmount(subtract);

		return orderItemEntity;


	}

}